package org.tankwar.view;

import org.tankwar.controller.GameMenuBarController;
import org.tankwar.controller.TankController;
import org.tankwar.model.*;
import org.tankwar.type.Direction;
import org.tankwar.type.GameType;
import org.tankwar.type.PlayerType;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 坦克大战游戏的面板类
 */
public class GamePanel extends JPanel {
    private final long init_time = new Date().getTime();
    private final MainFrame mainFrame; // 主窗口
    private static final long serialVersionUID = 5972735870004738773L;
    public static final int PANEL_WIDTH = 800; // 静态全局窗口大小
    public static final int PANEL_HEIGHT = 620;
    public boolean printable = true; // 记录暂停状态，此时线程不刷新界面
    public GameType gameType = GameType.TWO_PLAYER_MODE;
    JMenuBar jmb = null;
    JMenu jm1 = null, jm2 = null, jm3 = null, jm4 = null;
    JMenuItem jmi1 = null, jmi2 = null, jmi3 = null, jmi4 = null, jmi5 = null, jmi6 = null, jmi7 = null, jmi8 = null,
            jmi9 = null;
    Image screenImage = null;
    public List<PlayerTank> homeTanks = new ArrayList<PlayerTank>();
    public Blood blood = new Blood(); // 实例化血包
    public Home home = new Home(373, 545, this);// 实例化home

    // 以下集合变量在构造方法中进行了初始化
    public List<River> theRiver = new ArrayList<>();
    public List<Tank> tanks = new ArrayList<>();
    public List<BombTank> bombTanks = new ArrayList<>();
    public List<Bullets> bullets = new ArrayList<>();
    public List<Tree> trees = new ArrayList<>();
    public List<BrickWall> homeWall = new ArrayList<>(); // 实例化对象容器
    public List<BrickWall> otherWall = new ArrayList<>();
    public List<MetalWall> metalWall = new ArrayList<>();

    public GamePanel(MainFrame mainFrame) {
        this.mainFrame = mainFrame;
        // printable = false;
        // 创建菜单及菜单选项
        initJMenuBar();
        for (int i = 0; i < 10; i++) { // 家的格局
            if (i < 4)
                homeWall.add(new BrickWall(350, 580 - 21 * i, this)); // 基地左边的墙
            else if (i < 7)
                homeWall.add(new BrickWall(372 + 22 * (i - 4), 517, this)); // 基地上面的墙
            else
                homeWall.add(new BrickWall(416, 538 + (i - 7) * 21, this)); // 基地右边的墙
        }

        for (int i = 0; i < 32; i++) { // 砖墙
            if (i < 16) {
                otherWall.add(new BrickWall(220 + 20 * i, 300, this)); // 砖墙布局
                otherWall.add(new BrickWall(500 + 20 * i, 180, this));
                otherWall.add(new BrickWall(200, 400 + 20 * i, this));
                otherWall.add(new BrickWall(500, 400 + 20 * i, this));
            } else if (i < 32) {
                otherWall.add(new BrickWall(220 + 20 * (i - 16), 320, this));
                otherWall.add(new BrickWall(500 + 20 * (i - 16), 220, this));
                otherWall.add(new BrickWall(220, 400 + 20 * (i - 16), this));
                otherWall.add(new BrickWall(520, 400 + 20 * (i - 16), this));
            }
        }

        for (int i = 0; i < 20; i++) { // 金属墙布局
            if (i < 10) {
                metalWall.add(new MetalWall(140 + 30 * i, 150, this));
                metalWall.add(new MetalWall(600, 400 + 20 * (i), this));
            } else if (i < 20)
                metalWall.add(new MetalWall(140 + 30 * (i - 10), 180, this));
            else
                metalWall.add(new MetalWall(500 + 30 * (i - 10), 160, this));
        }

        for (int i = 0; i < 4; i++) { // 树的布局
            if (i < 4) {
                trees.add(new Tree(0 + 30 * i, 360, this));
                trees.add(new Tree(220 + 30 * i, 360, this));
                trees.add(new Tree(440 + 30 * i, 360, this));
                trees.add(new Tree(660 + 30 * i, 360, this));
            }
        }
        theRiver.add(new River(85, 100, this)); // 河流的位置
        for (int i = 0; i < 20; i++) { // 初始化20辆坦克 TODO 这个坦克数量应该是可以更改的
            if (i < 9) // 设置坦克出现的位置
                tanks.add(new EnemyTank(150 + 70 * i, 40, Direction.D, this));
            else if (i < 15)
                tanks.add(new EnemyTank(700, 140 + 50 * (i - 6), Direction.D, this));
            else
                tanks.add(new EnemyTank(10, 50 * (i - 12), Direction.D, this));
        }
        homeTanks.add(new PlayerTank(300, 560, Direction.STOP, this,
                PlayerType.PLAYER_ONE));// 实例化坦克 玩家1
        if (gameType == GameType.TWO_PLAYER_MODE)
            // 玩家2
            homeTanks.add(new PlayerTank(450, 560, Direction.STOP, this, PlayerType.PLAYER_TWO));
        mainFrame.addKeyListener(new TankController(this));// 键盘监听 TODO 这里可以使用“适配器模式”
        new Thread(new PaintThread()).start(); // 线程启动
    }

    /**
     * 初始化菜单条
     */
    private void initJMenuBar() {
        GameMenuBarController menuBarController = new GameMenuBarController(this);
        jmb = new JMenuBar();
        // 设置菜单显示的字体
        UIManager.put("Menu.font", new Font("微软雅黑", Font.BOLD, 18));
        // 设置菜单项的字体
        UIManager.put("MenuItem.font", new Font("微软雅黑", Font.BOLD, 16));
        jm1 = new JMenu("游戏");
        jm2 = new JMenu("暂停/继续");
        jm3 = new JMenu("帮助");
        jm4 = new JMenu("游戏级别");
        jmi1 = new JMenuItem("开始新游戏");
        jmi2 = new JMenuItem("退出");
        jmi3 = new JMenuItem("暂停");
        jmi4 = new JMenuItem("继续");
        jmi5 = new JMenuItem("游戏说明");
        jmi6 = new JMenuItem("级别1");
        jmi7 = new JMenuItem("级别2");
        jmi8 = new JMenuItem("级别3");
        jmi9 = new JMenuItem("级别4");
        jmi1.addActionListener(menuBarController);
        jmi1.setActionCommand("NewGame");
        jmi2.addActionListener(menuBarController);
        jmi2.setActionCommand("Exit");
        jmi3.addActionListener(menuBarController);
        jmi3.setActionCommand("Stop");
        jmi4.addActionListener(menuBarController);
        jmi4.setActionCommand("Continue");
        jmi5.addActionListener(menuBarController);
        jmi5.setActionCommand("help");
        jmi6.addActionListener(menuBarController);
        jmi6.setActionCommand("level1");
        jmi7.addActionListener(menuBarController);
        jmi7.setActionCommand("level2");
        jmi8.addActionListener(menuBarController);
        jmi8.setActionCommand("level3");
        jmi9.addActionListener(menuBarController);
        jmi9.setActionCommand("level4");
        jm1.add(jmi1);
        jm1.add(jmi2);
        jm2.add(jmi3);
        jm2.add(jmi4);
        jm3.add(jmi5);
        jm4.add(jmi6);
        jm4.add(jmi7);
        jm4.add(jmi8);
        jm4.add(jmi9);
        jmb.add(jm1);
        jmb.add(jm2);
        jmb.add(jm4);
        jmb.add(jm3);
    }

    private class PaintThread implements Runnable { // 定义一个画画线程类  内部类
        public void run() {
            while (printable) {
                repaint(); // 调用外部类的repaint方法进行重新绘制
                try {
                    Thread.sleep(50); // 线程没50毫秒睡一次，让游戏“动起来”
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public PaintThread getPaintThread() {
        return new PaintThread();
    }

    // 这是一个重写的方法,将由repaint()方法自动调用
    @Override
    public void paint(Graphics g) { // 这是继承自Window基类中的方法
        // 创建一幅用于双缓冲、可以在屏幕外绘制图像的缓存空间
        screenImage = this.createImage(PANEL_WIDTH, PANEL_HEIGHT);
        Graphics gps = screenImage.getGraphics();
        Color c = gps.getColor();
        gps.setColor(Color.GRAY);
        gps.fillRect(0, 0, PANEL_WIDTH, PANEL_HEIGHT);
        gps.setColor(c);
        framePaint(gps);
        // 先将所有的东西画到一幅图像里，最后一次性绘制到屏幕上
        g.drawImage(screenImage, 0, 0, null);
    }

    //
    public void framePaint(Graphics g) {
        Color c = g.getColor();
        g.setColor(new Color(40, 230, 150)); // 设置字体显示属性
        Font f1 = g.getFont();
        g.setFont(new Font("宋体", Font.BOLD, 20));
        g.drawString("区域内还有敌方坦克: ", 200, 30);
        g.setFont(new Font("微软雅黑", Font.ITALIC, 30));
        g.drawString("" + tanks.size(), 400, 30);
        g.setFont(new Font("宋体", Font.BOLD, 20));
        g.drawString("剩余生命值: ", 500, 30);
        g.setFont(new Font("微软雅黑", Font.ITALIC, 30));
        g.drawString("" + homeTanks.get(0).getLife(), 650, 30);
        g.setFont(new Font("TimesRoman", Font.BOLD, 20));
        g.drawString("游戏时间:" + (new Date().getTime() - init_time) / 1000, 5, 30);
        g.setFont(f1);
        // 如果玩家赢了（条件是敌方坦克全灭、大本营健在、玩家坦克仍有血量）
        if (tanks.size() == 0 && home.isLive() && (homeTanks.get(0).isLive()
                || (homeTanks.size() > 1 && homeTanks.get(1).isLive()))) {
            Font f = g.getFont();
            g.setFont(new Font("TimesRoman", Font.BOLD, 60));
            this.otherWall.clear();
            g.drawString("你赢了！ ", 310, 300);
            g.setFont(f);
        }

        if (!homeTanks.get(0).isLive() && (homeTanks.size() <= 1 || !homeTanks.get(1).isLive())) {
            Font f = g.getFont();
            g.setFont(new Font("仿宋", Font.BOLD, 40));
            // 修复bug：如果玩家的坦克死了，那么基地就没了
            home.setLive(false);
            g.setFont(f);
        }
        g.setColor(c);
        drawRelevantEffect(g);
    }

    public MainFrame getMainFrame() {
        return mainFrame;
    }

    /**
     * 画出游戏的各种组件 河流、树、墙、坦克、子弹、
     * @param g 画笔
     */
    public void drawRelevantEffect(Graphics g) {
        home.draw(g); // 画出home
        //画出己方坦克，设计其与河，家，树，metalWall，otherWall，homeWall的碰撞效果
        drawPlayerTanks(g);
        //画出敌方坦克，设计其与河，家，树，metalWall，otherWall，homeWall的碰撞效果
        drawEnemyTanks(g);
        drawTempEffect(g, theRiver); //画出河
        blood.draw(g);// 画出加血包
        drawTempEffect(g, trees);// 画出trees
        drawTempEffect(g, bombTanks);// 画出爆炸效果
        drawTempEffect(g, otherWall);// 画出otherWall
        drawTempEffect(g, metalWall);// 画出metalWall
        //画出子弹，设计其与我方坦克，敌方坦克，河，墙等碰撞效果
        handleAndDrawBullets(g);
    }

    /**
     * 画出子弹
     * @param g 画笔
     */
    private void handleAndDrawBullets(Graphics g) {
        // 这个也不能用增强for循环
        for (int i = 0; i < bullets.size(); i++) { // 对每一个子弹
            Bullets m = bullets.get(i);
            m.hitTanks(tanks); // 每一个子弹打到坦克上
            for (PlayerTank playerTank : homeTanks) {
                m.hitTank(playerTank); // 每一个子弹打到自己家的坦克上时
            }
            m.hitHome(); // 每一个子弹打到家里时
            for (MetalWall mw : metalWall) { // 每一个子弹打到金属墙上
                m.hitWall(mw); // TODO hitWall没起到作用
            }
            // 这个不能用增强for循环，会出现ConcurrentModificationException异常
            for (int j = 0; j < otherWall.size(); j++) {// 每一个子弹打到其他墙上
                m.hitWall(otherWall.get(j));
            }
            for (int j = 0; j < homeWall.size(); j++) {// 每一个子弹打到家的墙上
                m.hitWall(homeWall.get(j));
            }
            m.draw(g); // 画出效果图
        }
    }

    /**
     * 画出己方坦克，并处理碰撞、捡道具
     * @param g 画笔
     */
    private void drawPlayerTanks(Graphics g) {
        //画出己方坦克并初始化生命值
        for (PlayerTank playerTank : homeTanks) {
            playerTank.eat(blood);// 加血--生命值
        }
        //己方坦克碰上水
        handleTankCollision(homeTanks, homeTanks); // 处理玩家坦克撞到玩家坦克
        handleTankCollision(homeTanks, tanks); // 处理玩家坦克撞到敌方坦克
        handleRiverCollision(homeTanks, theRiver); // 处理玩家坦克撞到河流
        drawTempEffect(g, theRiver); // 画出河流
        handleMWallCollision(homeTanks, metalWall);//己方坦克撞到金属墙
        handleBWallCollision(homeTanks, otherWall);//己方坦克撞到otherWall
        handleBWallCollision(homeTanks, homeWall);//己方坦克撞到自己家
        handleHomeCollision(g, homeTanks, home);  // 处理玩家坦克撞到基地，并画出玩家坦克
    }

    /**
     * 画河流、树的方法
     * @param g 画笔
     * @param components 要画的游戏组件列表
     * @param <T> 泛型 继承游戏组件接口
     */
    public <T extends GameComponent> void drawTempEffect(Graphics g, List<T> components) {
        for (int i = 0; i < components.size(); i++) { // 不要用for循环
            components.get(i).draw(g);
        }
    }

    /**
     * 处理坦克撞到砖墙的方法
     * @param tanks 坦克列表
     * @param brickWalls 砖墙列表
     * @param <T> 坦克类型
     */
    public<T extends Tank> void handleBWallCollision(List<T> tanks, List<BrickWall> brickWalls) {
        for (T tank: tanks) {
            for (BrickWall brickWall: brickWalls) {
                tank.collideWithWall(brickWall);
            }
        }
    }

    /**
     * 处理坦克撞到金属墙的方法
     * @param tanks 坦克列表
     * @param metalWalls 金属墙列表
     * @param <T> 坦克类型
     */
    public<T extends Tank> void handleMWallCollision(List<T> tanks, List<MetalWall> metalWalls) {
        for (T tank: tanks) {
            for (MetalWall metalWall: metalWalls) {
                tank.collideWithWall(metalWall);
            }
        }
    }

    /**
     * 处理坦克撞到河流的方法
     * @param tanks 坦克列表
     * @param rivers 河流列表
     * @param <T> 坦克类型
     */
    public<T extends Tank> void handleRiverCollision(List<T> tanks, List<River> rivers) {
        for (T tank: tanks) {
            for (River river: rivers) {
                tank.collideRiver(river);
            }
        }
    }

    /**
     * 处理坦克撞到坦克的方法
     * @param fromTanks 撞击坦克列表
     * @param toTanks 被撞击坦克列表
     * @param <T> 撞击坦克类型
     * @param <E> 被撞击坦克类型
     */
    public<T extends Tank, E extends  Tank> void handleTankCollision(List<T> fromTanks, List<E> toTanks) {
        for (T fromTank: fromTanks) {
            fromTank.collideWithTanks(toTanks);
        }
    }

    /**
     * 处理坦克撞到基地的方法，并画出坦克
     * @param g 画笔
     * @param tanks 坦克列表
     * @param home 基地
     * @param <T> 坦克类型
     */
    public<T extends Tank> void handleHomeCollision(Graphics g, List<T> tanks, Home home) {
        for (int i = 0; i < tanks.size(); i++) {
            tanks.get(i).collideHome(home);
            tanks.get(i).draw(g);
        }
    }

    /**
     * 画出所有的地方坦克
     * @param g 画笔
     */
    public void drawEnemyTanks(Graphics g) {
        handleMWallCollision(tanks, metalWall);         // 敌方坦克撞到金属墙
        handleBWallCollision(tanks, otherWall);         // 敌方坦克撞到砖墙
        handleBWallCollision(tanks, homeWall);          // 敌方坦克撞到基地旁边的墙
        handleRiverCollision(tanks, theRiver);          // 敌方坦克撞到河流时
        handleTankCollision(tanks, tanks);              // 敌方坦克撞到其他敌方坦克
        handleHomeCollision(g, tanks, home);            // 敌方坦克撞到基地，并画出敌方坦克
    }
}